package spynx.desl.runtime.impl;

import spynx.desl.Expression;
import spynx.desl.StatementExpression;
import spynx.desl.runtime.IDataStorage;
import spynx.desl.runtime.IExecutionContext;
import spynx.desl.runtime.IObjectVariable;
import spynx.desl.runtime.IRuntimeEntity;
import spynx.desl.runtime.IVariable;
import spynx.desl.runtime.IVariableFrame;
import spynx.util.DeslRuntimeUtil;

public abstract class AbstractExecutionContext implements IExecutionContext, IVariable {	
	protected IVariableFrame vars;
	protected IExecutionContext parent;
	private String name;
	
	public AbstractExecutionContext(IExecutionContext parent) {
		vars = new VariableFrame(parent != null ? parent.Variables() : null);
		this.parent = parent;
	}
	
	@Override
	public IExecutionContext getParent() {
		return parent;
	}
	
	@Override
	public IExecutionContext getRootContext() {
		IExecutionContext root = this;
		for (;root.getParent() != null; root = root.getParent());
		return root;
	}
	
	@Override
	public IVariableFrame Variables() {
		return this.vars;
	}

	@Override
	public IVariable createVariable(String name, Object value) {
		IVariable var = new Variable(name, value);
		if (name != null)
			vars.addVariable(var);
		return var;
	}
	
	@Override
	public IVariable createVariable(String name, Class<? extends IVariable> clazz, Object value) {
		try {
			IVariable var = clazz.newInstance();
			var.setName(name);
			var.setValue(value);
			if (name != null)
				vars.addVariable(var);
			return var;
		} catch (Exception e) {
			throw new RuntimeException("Unable to create variable of class " + clazz);
		}
	}

	@Override
	public abstract IObjectVariable createObjectVariable(String name, String className);

	@Override
	public IRuntimeEntity resolveIdentifier(String ident) {
		if ("context".equals(ident))
			return this;
		if ("arguments".equals(ident))
			return DeslRuntimeUtil.getMatchInfo(this).getArguments();
		if ("this".equalsIgnoreCase(ident))
			ident = "@this";
		IVariable var = vars.getVariable(ident);
		return var != null ? var : parent != null ? parent.resolveIdentifier(ident) : null;
	}
	
	@Override
	public Object resolveValue(Object value) {
		if (value instanceof StatementExpression)
			return value;
		if (value instanceof Expression)
			value = resolveValue(((Expression) value).execute(this));
		if (value instanceof IDataStorage) {
			Object v = ((IDataStorage) value).getValue();
			return v != value ? resolveValue(v) : v;
		}
		return value;
	}
	
	@Override
	public IDataStorage resolveReference(Object value) {
		if (value instanceof Expression)
			return resolveReference(((Expression) value).execute(this));
			//value = ((Expression) value).execute(this);
		if (value instanceof IDataStorage)
			return (IDataStorage) value;
		return null;
	}

	@Override
	public void clearContext() {
		vars.clearTransientVars();
	}
	
	@Override
	public String getName() {
		return this.name;
	}
	
	@Override
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public boolean isReadOnly() {
		return true;
	}
	
	@Override
	public void setReadOnly(boolean readonly) {
	}
	
	@Override
	public boolean isPersist() {
		return true;
	}
	
	@Override
	public Object getValue() {
		return this;
	}
	
	@Override
	public void setValue(Object value) {
		
	}
	
	@Override
	public Object applyOperator(IExecutionContext context, String operator,
			Object... args) {
		return null;
	}
}
